package simpledb.server.drivers;

import simpledb.file.Block;
import simpledb.server.SimpleDB;
import simpledb.tx.Transaction;

public class QChkptTestDriver {
    final static String fileName = "testdbdir.tbl";
    static Transaction initializer;
    static int blockOffset = 4;

    /*
    * The 3 different types of transactions.
    */
    static final int COMMIT = 1, ROLLBACK = 2, UNDO = 3;

    public static void doRecovery() { // Reading from blocks.
        String header = "***** SYSTEM RECOVERY STARTED *****";
        String footer = "***** SYSTEM RECOVERY FINISHED *****";
        String line = "#######################################################################";
        initializer.recover();
        System.out.println(header);
        System.out.println(line);
        Block[] blks = new Block[6];
        int blockOffset = 4;

        for (int i = 0; i < 6; i++) {
            blks[i] = new Block(fileName, i);
        }

        Transaction t = new Transaction();

        for (int i = 0; i < 6; i++) {
            t.pin(blks[i]);
            int a = t.getInt(blks[i], blockOffset);
            System.out.println(blks[i] + "  -->  " + a);
            t.unpin(blks[i]);
        }
        System.out.println(line);
        System.out.println(footer);
        initializer.commit();
    }

    public static void doRecovery1() { // Reading from blocks.
        SimpleDB.init("dbdir");
        String filename = "testdbdir.tbl";
        Block[] blks = new Block[6];
        int blockOffset = 4;

        for (int i = 0; i < 6; i++) {
            blks[i] = new Block(filename, i);
        }

        Transaction t = new Transaction();

        for (int i = 0; i < 6; i++) {
            t.pin(blks[i]);
            int a = t.getInt(blks[i], blockOffset);
            System.out.println(blks[i] + "  -->  " + a);
            t.unpin(blks[i]);
        }
        t.undo();
    }


    public static void main(String[] args) {

        SimpleDB.init("dbdir");
        initializer = new Transaction();
        Block[] blocks = new Block[6];
        // Initializing blocks.
        for (int i = 0; i < 6; i++) {
            blocks[i] = new Block(fileName, i);
            initializer.pin(blocks[i]);
            initializer.setInt(blocks[i], blockOffset, 111 * (i + 1));
        }
        initializer.commit();


        Thread[] threads = new Thread[6];

        threads[0] = new TestThreadSingle(0, 1, 100, 200, COMMIT);
        threads[1] = new TestThreadSingle(2, 3, 300, 400, COMMIT);
        threads[2] = new TestThreadSingle(4, 5, 500, 600, ROLLBACK);
        threads[3] = new TestThreadSingle(0, 1, 700, 800, UNDO);
        threads[4] = new TestThreadSingle(2, 3, 900, 1000, COMMIT);
        threads[5] = new TestThreadSingle(4, 5, 1100, 1200, UNDO);
		/*threads[0] = new TestThreadSingle(0, 1, 100, 200, COMMIT);
		threads[1] = new TestThreadSingle(1, 2, 300, 400, COMMIT);
		threads[2] = new TestThreadSingle(2, 3, 500, 600, ROLLBACK);
		threads[3] = new TestThreadSingle(3, 4, 700, 800, UNDO);
		threads[4] = new TestThreadSingle(4, 5, 900, 1000, COMMIT);
		threads[5] = new TestThreadSingle(5, 0, 1100, 1200, UNDO);*/

        for (Thread t : threads) {
            t.start();
        }

        for (Thread t : threads) {
            try {
                t.join();
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
        System.out.println("Ending Main ... ");

        //doRecovery1();
    }
}

class TestThreadSingle extends Thread { // Creates one transaction per thread.
    private int[] blockNums;
    private int[] values;
    private int transType;

    public TestThreadSingle(int block1, int block2, int value1, int value2, int type) {
        blockNums = new int[2];
        values = new int[2];
        blockNums[0] = block1;
        blockNums[1] = block2;
        values[0] = value1;
        values[1] = value2;
        transType = type;
    }

    @Override
    public void run() {
        // TODO Auto-generated method stub
        Transaction tx = new Transaction();
        Block blk1 = new Block(QChkptTestDriver.fileName, blockNums[0]);
        Block blk2 = new Block(QChkptTestDriver.fileName, blockNums[1]);

        tx.pin(blk1);
        tx.pin(blk2);

        //Updating the two blocks.
        tx.setInt(blk1, QChkptTestDriver.blockOffset, values[0]);
        tx.setInt(blk2, QChkptTestDriver.blockOffset, values[1]);
        String line = "#######################################################################";
        String status = line;
        status += "\nTransaction#: " + tx.getTxnum() + " writing " + values[0] + " to: " + blk1;
        status += "\nTransaction#: " + tx.getTxnum() + " writing " + values[1] + " to: " + blk2;
        status += "\nTransaction#: " + tx.getTxnum() + " will be ";

        switch (transType) {
            case QChkptTestDriver.COMMIT:
                tx.commit();
                status += "committed.";
                break;
            case QChkptTestDriver.ROLLBACK:
                tx.rollback();
                status += "rolled back.";
                break;

            case QChkptTestDriver.UNDO:
                //tx.undo();
                status += "undone.";
                break;

            default:
                break;
        }
        status += "\n" + line;
        //Print status of this transaction.
        System.out.println(status);
    }
}
